VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "ApiPrinter"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' ##MODULE_DESCRIPTION This class provides the properties and methods _
for a getting information about and controlling a printer or print queue.

' ##MODULE_DESCRIPTION You can use this to get information about the status _
of any of the printers installed and (subject to having access rights) to _
control that printer.

' ##ENUMERATION_DESCRIPTION Printer_Control_Commands are sent to the printer to perform actions _
that control the current printer's job(s)
Public Enum Printer_Control_Commands
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_CONTROL_PAUSE Pause the printing of the currently active job
     PRINTER_CONTROL_PAUSE = 1
     ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_CONTROL_RESUME Resume printing if paused
     PRINTER_CONTROL_RESUME = 2
     ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_CONTROL_PURGE Terminate and delete the currently printing job
     PRINTER_CONTROL_PURGE = 3
     ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_CONTROL_SET_STATUS Set the printer status for the current job
     PRINTER_CONTROL_SET_STATUS = 4
End Enum

' ##ENUMERATION_DESCRIPTION Printer_Status are the various possible states a printer can be in. Note _
that this information is provided by the printer driver and in many cases the status will always be _
"Ready" unless there is one or more jobs in error on that printer.
Public Enum Printer_Status
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_READY The printer is free and ready to print
    PRINTER_STATUS_READY = &H0
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PAUSED Printing is paused
    PRINTER_STATUS_PAUSED = &H1
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_ERROR Printing is suspended due to a general error
    PRINTER_STATUS_ERROR = &H2
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PENDING_DELETION Printing is suspended while a job is being deleted
    PRINTER_STATUS_PENDING_DELETION = &H4
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PAPER_JAM Printing is suspended because there has been a paper jam
    PRINTER_STATUS_PAPER_JAM = &H8
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PAPER_OUT Printing is suspended because the printer has run out of paper
    PRINTER_STATUS_PAPER_OUT = &H10
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_MANUAL_FEED Printing is suspended pending a manual paper feed
    PRINTER_STATUS_MANUAL_FEED = &H20
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PAPER_PROBLEM Printing is supended due to a paper problem
    PRINTER_STATUS_PAPER_PROBLEM = &H40
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_OFFLINE The printer is off line
    PRINTER_STATUS_OFFLINE = &H80
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_IO_ACTIVE The printer is reading data from the IO port
    PRINTER_STATUS_IO_ACTIVE = &H100
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_BUSY The printer is busy
    PRINTER_STATUS_BUSY = &H200
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PRINTING The printer is active and printing a job
    PRINTER_STATUS_PRINTING = &H400
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_OUTPUT_BIN_FULL The output tray is full and the printer has paused until it is emptied
    PRINTER_STATUS_OUTPUT_BIN_FULL = &H800
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_NOT_AVAILABLE The printer status is not known
    PRINTER_STATUS_NOT_AVAILABLE = &H1000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_WAITING The printer is waiting for a job
    PRINTER_STATUS_WAITING = &H2000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PROCESSING The printer is active and processing a print job
    PRINTER_STATUS_PROCESSING = &H4000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_INITIALIZING The printer is initialising and not yet ready to print
    PRINTER_STATUS_INITIALIZING = &H8000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_WARMING_UP The printer is warming up and not yet ready to print
    PRINTER_STATUS_WARMING_UP = &H10000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_TONER_LOW Printing is suspended because the level of toner or ink is too low for a reasonable quality print
    PRINTER_STATUS_TONER_LOW = &H20000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_NO_TONER Printing has been suspended because the printer is out of toner or ink
    PRINTER_STATUS_NO_TONER = &H40000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_PAGE_PUNT (Win95 only) The printer is suspended while a page is deleted
    PRINTER_STATUS_PAGE_PUNT = &H80000 'win95
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_USER_INTERVENTION The printer has been paused by the user intervention
    PRINTER_STATUS_USER_INTERVENTION = &H100000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_OUT_OF_MEMORY Printing is suspended because the printer has run out of memory
    PRINTER_STATUS_OUT_OF_MEMORY = &H200000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_DOOR_OPEN Printing is supended because a door on the printer is open
    PRINTER_STATUS_DOOR_OPEN = &H400000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_SERVER_UNKNOWN Printing is suspended due to an unknown server error
    PRINTER_STATUS_SERVER_UNKNOWN = &H800000
    ' ##ENUMERATION_MEMBER_DESCRIPTION PRINTER_STATUS_POWER_SAVE The printer is suspended in power saving mode
    PRINTER_STATUS_POWER_SAVE = &H1000000
End Enum

Public Enum Printer_Attributes
     PRINTER_ATTRIBUTE_QUEUED = &H1
     PRINTER_ATTRIBUTE_DIRECT = &H2
     PRINTER_ATTRIBUTE_DEFAULT = &H4
     PRINTER_ATTRIBUTE_SHARED = &H8
     PRINTER_ATTRIBUTE_NETWORK = &H10
     PRINTER_ATTRIBUTE_HIDDEN = &H20
     PRINTER_ATTRIBUTE_LOCAL = &H40
     PRINTER_ATTRIBUTE_ENABLE_DEVQ = &H80
     PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS = &H100
     PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST = &H200
     PRINTER_ATTRIBUTE_WORK_OFFLINE = &H400
     PRINTER_ATTRIBUTE_ENABLE_BIDI = &H800
     PRINTER_ATTRIBUTE_RAW_ONLY = &H1000
     PRINTER_ATTRIBUTE_PUBLISHED = &H2000
End Enum

Private Type PRINTER_INFO_1
    flags As Long
    pDescription As String
    pName As String
    pComment As String
End Type

Private Type PRINTER_INFO_2
    pServerName As String
    pPrinterName As String
    pShareName As String
    pPortName As String
    pDriverName As String
    pComment As String
    pLocation As String
    pDevMode As Long 'LPDEVMODEA
    pSepFile As String
    pPrintProcessor As String
    pDatatype As String
    pParameters As String
    pSecurityDescriptor As Long 'PSECURITY_DESCRIPTOR
    Attributes As Long
    Priority As Long
    DefaultPriority As Long
    StartTime As Long
    UntilTime As Long
    Status As Long
    JobsCount As Long
    AveragePPM As Long
End Type


Private Type PRINTER_INFO_3
    pSecurityDescriptor As Long 'PSECURITY_DESCRIPTOR
End Type

Private Type PRINTER_INFO_4
    pPrinterName As String
    pServerName As String
    Attributes As Long
End Type

Private Type PRINTER_INFO_5
    pPrinterName As String
    pPortName As String
    Attributes As Long
    DeviceNotSelectedTimeout As Long
    TransmissionRetryTimeout As Long
End Type

Private Type PRINTER_INFO_6
    dwStatus As Long
End Type

Private Type PRINTER_INFO_7
    pszObjectGUID As String
    dwAction As Long
End Type

Private Type PRINTER_INFO_8
    pDevMode As Long 'DEVMODE
End Type

'api calls...

Private Declare Function SetPrinterApi Lib "winspool.drv" Alias "SetPrinterA" (ByVal hPrinter As Long, _
                                                                                     ByVal Level As Long, _
                                                                                     buffer As Any, _
                                                                                     ByVal command As Long) As Long
                                                                                     
                                                                                     
    
'Aliased api calls....
Private Declare Function GetPrinterApi Lib "winspool.drv" Alias _
                           "GetPrinterA" (ByVal hPrinter As Long, _
                                            ByVal Level As Long, _
                                            buffer As Long, _
                                            ByVal pbSize As Long, _
                                            pbSizeNeeded As Long) As Long


Private Type DEVMODE
    dmDeviceName As String * 32
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * 32
    dmUnusedPadding As Integer
    dmBitsPerPixel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    ' The following only appear in Windows 95, 98, 2000
    dmICMMethod As Long
    dmICMIntent As Long
    dmMediaType As Long
    dmDitherType As Long
    dmReserved1 As Long
    dmReserved2 As Long
    ' The following only appear in Windows 2000
    dmPanningWidth As Long
    dmPanningHeight As Long
End Type

Private Type PRINTER_DEFAULTS
  pDatatype As String
  pDevMode As DEVMODE
  DesiredAccess As Long
End Type

Private Declare Function OpenPrinter Lib "winspool.drv" _
        Alias "OpenPrinterA" (ByVal pPrinterName As String, _
        phPrinter As Long, pDefault As PRINTER_DEFAULTS) As Long

Private Declare Function ClosePrinter Lib "winspool.drv" _
        (ByVal hPrinter As Long) As Long


'\\ Printer handle....
Private mhPrinter As Long

'\\ Private member variables for simplicity sake....
Private mPRINTER_INFO_1 As PRINTER_INFO_1
Private mPRINTER_INFO_2 As PRINTER_INFO_2
Private mPRINTER_INFO_3 As PRINTER_INFO_3
Private mPRINTER_INFO_4 As PRINTER_INFO_4
Private mPRINTER_INFO_5 As PRINTER_INFO_5
Private mPRINTER_INFO_6 As PRINTER_INFO_6
Private mPRINTER_INFO_7 As PRINTER_INFO_7
Private mPRINTER_INFO_8 As PRINTER_INFO_8
Private mPRINTER_INFO_9 As PRINTER_INFO_8 'currently this structure is identical 9 and 8

'\\ Declaration to enumerate the active jobs on this printer
Private Declare Function EnumJobs Lib "winspool.drv" Alias "EnumJobsA" _
                          (ByVal hPrinter As Long, _
                           ByVal FirstJob As Long, _
                           ByVal NoJobs As Long, _
                           ByVal Level As Long, _
                           pJob As Long, _
                           ByVal cdBuff As Long, _
                           pcbNeeded As Long, _
                           pcBytesReturned As Long) As Long
                           
'\\ To get a printer's device context....
Private Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As DEVMODE) As Long
Private Declare Function CreateDCByLong Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long

Private Property Get Attributes() As Long

Call RefreshPrinterInfo(2)
Attributes = mPRINTER_INFO_2.Attributes

End Property

Public Property Get AveragePagesPerMonth() As Long

Call RefreshPrinterInfo(2)
AveragePagesPerMonth = mPRINTER_INFO_2.AveragePPM

End Property

Public Property Get Comment() As String

Call RefreshPrinterInfo(1)
Comment = mPRINTER_INFO_1.pComment

End Property

Public Property Get DataType() As String

Call RefreshPrinterInfo(2)
DataType = mPRINTER_INFO_2.pDatatype

End Property


Public Property Get DefaultPriority() As Long

Call RefreshPrinterInfo(2)
DefaultPriority = mPRINTER_INFO_2.DefaultPriority

End Property

Public Property Get Description() As String

Call RefreshPrinterInfo(1)
Description = mPRINTER_INFO_1.pDescription

End Property


Public Property Get DeviceContext() As ApiDeviceContext

Dim hPrinterDC As Long
Dim DCPrinter As New ApiDeviceContext

hPrinterDC = CreateDCByLong("WINSPOOL", Me.DeviceName, vbNullString, vbNull)
If Err.LastDllError Then
    ReportError Err.LastDllError, "ApiPrinter:DeviceContext", GetLastSystemError
End If

Set DeviceContext = DCPrinter

End Property

Public Property Get DeviceName() As String

Call RefreshPrinterInfo(1)
DeviceName = mPRINTER_INFO_1.pName

End Property

Public Property Let DeviceName(ByVal newname As String)

Dim lRet As Long
Dim pDef As PRINTER_DEFAULTS

If mhPrinter = 0 Then
    '\\ Need to get the printer handle for the printer device named
    lRet = OpenPrinter(newname, mhPrinter, pDef)
End If

End Property

Public Property Get Direct() As Boolean

Direct = Attributes And PRINTER_ATTRIBUTE_DIRECT

End Property

Public Property Get DriverName() As String

Call RefreshPrinterInfo(2)
DriverName = mPRINTER_INFO_2.pDriverName

End Property

Public Property Get Location() As String

Call RefreshPrinterInfo(2)
Location = mPRINTER_INFO_2.pLocation

End Property

Public Property Get Parameters() As String

Call RefreshPrinterInfo(2)
Parameters = mPRINTER_INFO_2.pParameters

End Property

Public Property Get PendingJobsCount() As Long

Call RefreshPrinterInfo(2)
PendingJobsCount = mPRINTER_INFO_2.JobsCount

End Property

Public Property Get PortName() As String

Call RefreshPrinterInfo(2)
PortName = mPRINTER_INFO_2.pPortName

End Property

Friend Property Let PrinterHandle(ByVal newhandle As Long)

If newhandle <> mhPrinter Then
    mhPrinter = newhandle
End If

End Property


Public Property Get PrintJobs() As Collection

Dim colThis As Collection
Dim oPrintJob As ApiPrintJob

Dim lRet As Long
Dim pcbSizeRequired As Long, pcbBytesReturned As Long
Dim pJobId As Long

Dim buffer() As Long

'\\ Initialise the collection of print job objects
Set colThis = New Collection

ReDim Preserve buffer(0) As Long

lRet = EnumJobs(mhPrinter, 0, 255, 1, buffer(0), UBound(buffer), pcbSizeRequired, pcbBytesReturned)

If pcbSizeRequired > 0 Then
    
    '\\ Need to resize our array to cope with this data
    ReDim Preserve buffer(0 To (pcbSizeRequired / 4) + 3) As Long
    lRet = EnumJobs(mhPrinter, 0, 255, 1, buffer(0), UBound(buffer) * 4, pcbSizeRequired, pcbBytesReturned)
    
    '\\ Note any error if this has failed
    If Err.LastDllError <> 0 Then
        Call ReportError(Err.LastDllError, "ApiPrinter:PrintJobs", GetLastSystemError)
    End If
    
    '\\ At this stage buffer() contains an array of JOB_INFO_1 structures
    '\\ For each job...
    For pJobId = 0 To (pcbBytesReturned - 1) 'each record is 16 bytes long
        Set oPrintJob = New ApiPrintJob
        With oPrintJob
            .PrinterHandle = mhPrinter
            .JobId = buffer(pJobId * 16)
        End With
        colThis.Add oPrintJob
    Next pJobId
    
End If

'\\ pass the collection to the caller
Set PrintJobs = colThis

End Property

Public Property Get PrintProcessor() As String

Call RefreshPrinterInfo(2)
PrintProcessor = mPRINTER_INFO_2.pPrintProcessor

End Property

Public Property Get Priority() As Long

Call RefreshPrinterInfo(2)
Priority = mPRINTER_INFO_2.Priority

End Property

Public Property Get Queued() As Boolean

Queued = Attributes And PRINTER_ATTRIBUTE_QUEUED

End Property


Private Sub RefreshPrinterInfo(ByVal index As Integer)

Dim lRet As Long
Dim SizeNeeded As Long

Dim buffer() As Long

ReDim Preserve buffer(0 To 1) As Long
lRet = GetPrinterApi(mhPrinter, index, buffer(0), UBound(buffer), SizeNeeded)
ReDim Preserve buffer(0 To (SizeNeeded / 4) + 3) As Long
lRet = GetPrinterApi(mhPrinter, index, buffer(0), UBound(buffer) * 4, SizeNeeded)


If index < 1 Or index > 9 Then
    Debug.Print "Error in call to ApiPrinter.RefreshPrinterInfo - invalid index"
Else
    Select Case index
    Case 1
        With mPRINTER_INFO_1
            .flags = buffer(0)
            .pDescription = StringFromPointer(buffer(1), 1024)
            .pName = StringFromPointer(buffer(2), 1024)
            .pComment = StringFromPointer(buffer(3), 1024)
        End With
    Case 2
        With mPRINTER_INFO_2
            .pServerName = StringFromPointer(buffer(0), 1024)
            .pPrinterName = StringFromPointer(buffer(1), 1024)
            .pShareName = StringFromPointer(buffer(2), 1024)
            .pPortName = StringFromPointer(buffer(3), 1024)
            .pDriverName = StringFromPointer(buffer(4), 1024)
            .pComment = StringFromPointer(buffer(5), 1024)
            .pLocation = StringFromPointer(buffer(6), 1024)
            .pDevMode = buffer(7)
            .pSepFile = StringFromPointer(buffer(8), 1024)
            .pPrintProcessor = StringFromPointer(buffer(9), 1024)
            .pDatatype = StringFromPointer(buffer(10), 1024)
            .pParameters = StringFromPointer(buffer(11), 1024)
            .pSecurityDescriptor = buffer(12)
            .Attributes = buffer(13)
            .Priority = buffer(14)
            .DefaultPriority = buffer(15)
            .StartTime = buffer(16)
            .UntilTime = buffer(17)
            .Status = buffer(18)
            .JobsCount = buffer(19)
            .AveragePPM = buffer(20)
        End With
    Case 3
        With mPRINTER_INFO_3
            .pSecurityDescriptor = buffer(0)
        End With
    Case 4
        With mPRINTER_INFO_4
            .pPrinterName = StringFromPointer(buffer(0), 1024)
            .pServerName = StringFromPointer(buffer(1), 1024)
            .Attributes = buffer(2)
        End With
    Case 5
        With mPRINTER_INFO_5
            .pPrinterName = StringFromPointer(buffer(0), 1024)
            .pPortName = StringFromPointer(buffer(1), 1024)
            .Attributes = buffer(2)
            .DeviceNotSelectedTimeout = buffer(3)
            .TransmissionRetryTimeout = buffer(4)
        End With
    Case 6
        With mPRINTER_INFO_6
            .dwStatus = buffer(0)
        End With
    Case 7
        With mPRINTER_INFO_7
            .pszObjectGUID = StringFromPointer(buffer(0), 1024)
            .dwAction = buffer(1)
        End With
    Case 8
        With mPRINTER_INFO_8
            .pDevMode = buffer(0)
        End With
    Case 9
        '\\ Currently this structure is the same as 8...
        With mPRINTER_INFO_9
            .pDevMode = buffer(0)
        End With
    End Select
    If Err.LastDllError <> 0 Then
        ReportError Err.LastDllError, "ApiPrinter:RefreshPrinterInfo", GetLastSystemError
    End If
End If

End Sub

Public Property Get SeparatorFile() As String

Call RefreshPrinterInfo(2)
SeparatorFile = mPRINTER_INFO_2.pSepFile

End Property

Public Property Get ServerName() As String

Call RefreshPrinterInfo(4)
ServerName = mPRINTER_INFO_4.pServerName

End Property


Public Property Get ShareName() As String

   Call RefreshPrinterInfo(2)
   ShareName = mPRINTER_INFO_2.pShareName

End Property


'\\ --[Status]---------------------------------------------------------------------------------
'\\ Returns the status of the selected printer when it is called.
'\\ -------------------------------------------------------------------------------------------
Public Property Get Status() As Printer_Status

    Call RefreshPrinterInfo(2)
    Status = mPRINTER_INFO_2.Status

End Property


